package main

import (
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"github.com/jinzhu/gorm"
	"github.com/pkg/errors"
	"golangtc/utils"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"

	_"github.com/go-sql-driver/mysql"
)

const (
	MysqlUrl     = "root:123456@tcp(127.0.0.1:3306)/juejin?charset=utf8mb4&parseTime=True&loc=Local"
	MysqlDebug   = true
	Limit        = 100
	SaltLength   = 6
	InitPassword = "123456"
	EmailSuffix  = "@qq.com"
	Host         = "https://golangtc.com"
)

var Db *gorm.DB

type BianchengSitemap struct {
	ID        int       `gorm:"column:id;primary_key" form:"id" json:"id"`
	Title     string    `form:"title" json:"title"` // 标题
	Urls      string    `form:"urls" json:"urls"`
	Status    int       `form:"status" json:"status"`
	Content   string    `form:"content" json:"content"`
	CreatedAt time.Time `form:"created_at" json:"created_at"`
	UpdatedAt time.Time `form:"updated_at" json:"updated_at"`
}

func NewMysql() (err error) {
	Db, err = gorm.Open("mysql", MysqlUrl)
	if err != nil {
		return errors.Wrap(err, "gorm open mysql connection error")
	}
	err = Db.DB().Ping()
	if err != nil {
		return  errors.Wrap(err, "mysql ping fail")
	}
	if MysqlDebug {
		Db = Db.Debug()
	}
	Db.DB().SetConnMaxLifetime(time.Minute * 10)
	return
}

type Thread struct {
	ID         int        `gorm:"column:id;primary_key" form:"id" json:"id"`
	CreatedAt  time.Time  `db:"created_at" json:"created_at"`
	UpdatedAt  time.Time  `db:"updated_at" json:"updated_at"`
	DeletedAt  *time.Time `db:"deleted_at" json:"deleted_at"`
	Title      string     `form:"title" json:"title"`
	Content    string     `form:"content" json:"content"`
	Tag        string     `form:"tag" json:"tag"`
	Username   string     `form:"username" json:"username"`
	Avatar     string     `form:"avatar" json:"avatar"`
	Digest     int        `form:"is_digest" json:"is_digest"`
	Top        int        `form:"top" json:"top"`
	Hot        int        `form:"hot" json:"hot"`
	ViewNum    int        `form:"view_num" json:"view_num"`
	CommentNum int        `form:"comment_num" json:"comment_num"`
}

func GetRequest(url string) (doc *goquery.Document) {
	//提交请求
	fmt.Println(url)
	request, err := http.NewRequest("GET", url, nil)
	request.Header.Add("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36")
	request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3")
	//处理返回结果
	client := &http.Client{}
	resp, err := client.Do(request)
	if err != nil {
		panic(err)
	}
	if resp.StatusCode != 200 {
		fmt.Println("err")
	}
	doc, err = goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		panic(err)
	}
	return
}

func GetThread() {
	offset := 0
	for {
		var value []Thread
		obj := Db.Table("bbs_thread")
		obj.
			Offset(offset * Limit).
			Limit(Limit).
			Find(&value)
		_ = ImportUser(value)
		if len(value) < Limit {
			break
		}
		offset += 1
	}
	return
}

func ImportUser(thread []Thread) (err error) {
	sql := "INSERT INTO `bbs_user` (`username`,`nickname`,`password`,`salt`,`email`,`avatar`,`created_ip`,`created_at`) VALUES "
	for key, value := range thread {
		salt := utils.CreateRandomString(SaltLength)
		if len(thread)-1 == key {
			sql += fmt.Sprintf("('%s','%s','%s','%s','%s','%s','%s','%s');",
				value.Username,
				value.Username,
				utils.CryptPassword("123456", salt),
				salt,
				value.Username+"@qq.com",
				value.Avatar,
				"127.0.0.1",
				time.Now().Format("2006/1/2 15:04:05"))
		} else {
			sql += fmt.Sprintf("('%s','%s','%s','%s','%s','%s','%s','%s'),",
				value.Username,
				value.Username,
				utils.CryptPassword(InitPassword, salt),
				salt,
				value.Username+EmailSuffix,
				value.Avatar,
				"127.0.0.1",
				time.Now().Format("2006/1/2 15:04:05"))
		}
	}
	err = Db.Exec(sql).Error
	return
}

// 获取siteMap
func SiteMap() (sitemap []BianchengSitemap) {
	page, maxPage := 1, 268
	var pageList []int
	for page <= maxPage {
		var urlList []string
		doc := GetRequest(strings.Replace(Host+"/?p={$page}", "{$page}", strconv.Itoa(page), -1))
		doc.Find(".box article").Each(func(i int, s *goquery.Selection) {
			url, _ := s.Find(".content .title a").Attr("href")
			urlList = append(urlList, Host+url)
			log.Printf("url = %v\n", Host+url)
		})
		if len(urlList) == 0 {
			break
		}
		err := SiteMapImport(urlList)
		if err != nil {
			pageList = append(pageList, page)

		}
		page++
	}
	return
}

func Link() {
	page, maxPage := 1, 1
	var pageList []int
	for page <= maxPage {
		var linkList []string
		doc := GetRequest("https://golangtc.com/link")
		doc.Find(".buttons .button").Each(func(i int, s *goquery.Selection) {
			url, _ := s.Attr("href")
			desc, _ := s.Attr("title")
			title := s.Text()
			linkList = append(linkList, Host+url)
			sql := "INSERT INTO `bbs_link` (`title`,`desc`,`url`,`created_at`,`updated_at`) VALUES "
			sql += fmt.Sprintf("('%s','%s','%s','%s','%s');",
				title,
				desc,
				url,
				time.Now().Format("2006/1/2 15:04:05"),
				time.Now().Format("2006/1/2 15:04:05"))
			err := Db.Exec(sql).Error
			if err != nil {
				pageList = append(pageList, page)
			}
		})
		if len(linkList) == 0 {
			break
		}
		page++
	}
	return
}

func SiteMapImport(urlList []string) (err error) {
	sql := "INSERT INTO `bbs_thread` (`title`,`content`,`username`,`avatar`,`view_num`,`comment_num`,`tag`,`created_at`,`updated_at`) VALUES "
	for key, value := range urlList {
		doc := GetRequest(value)
		content, _ := doc.Find(".media-content .content:nth-of-type(2)").Html()
		title := doc.Find(".media-content .content:nth-of-type(1) h1").Text()
		createTime, _ := doc.Find(".media-content .metadata .media-content .content .is-grouped .control:nth-of-type(1) time").Attr("datetime")
		viewNum := doc.Find(".media-content .metadata .media-content .content .is-grouped .control:nth-of-type(2) span:nth-of-type(2)").Text()
		commentNum := doc.Find(".media-content .metadata .media-content .content .is-grouped .control:nth-of-type(3) span:nth-of-type(2)").Text()
		tag := doc.Find(".media-content .metadata .media-content .content .is-grouped .control:nth-of-type(4) a").Text()
		username := doc.Find(".media-content .metadata .media-content .content a strong").Text()
		avatar, _ := doc.Find(".media-content .metadata .media-left  a img").Attr("src")
		title = strings.Replace(title, "'", "\\'", -1)
		title = strings.Replace(title, "amp;", "", -1)
		content = strings.Replace(content, "'", "\\'", -1)
		content = strings.Replace(content, "amp;", "", -1)
		if len(urlList)-1 == key {
			sql += fmt.Sprintf("('%s','%s','%s','%s','%s','%s','%s','%s','%s');",
				title,
				content,
				username,
				avatar,
				viewNum,
				commentNum,
				tag,
				createTime,
				time.Now().Format("2006/1/2 15:04:05"))
		} else {
			sql += fmt.Sprintf("('%s','%s','%s','%s','%s','%s','%s','%s','%s'),",
				title,
				content,
				username,
				avatar,
				viewNum,
				commentNum,
				tag,
				createTime,
				time.Now().Format("2006/1/2 15:04:05"))
		}
	}
	err = Db.Exec(sql).Error
	return
}

func CategoryImport() (err error) {
	var tag []string
	err = Db.Table("bbs_thread").Select("tag").Group("tag").Pluck("tag", &tag).Error
	if len(tag) > 0 {
		sql := "INSERT INTO `bbs_category` (`name`,`pid`,`created_at`,`updated_at`) VALUES "
		for key, value := range tag {
			if len(tag)-1 == key {
				sql += fmt.Sprintf("('%s','%d','%s','%s');",
					value,
					0,
					time.Now().Format("2006/1/2 15:04:05"),
					time.Now().Format("2006/1/2 15:04:05"))
			} else {
				sql += fmt.Sprintf("('%s','%d','%s','%s'),",
					value,
					0,
					time.Now().Format("2006/1/2 15:04:05"),
					time.Now().Format("2006/1/2 15:04:05"))
			}
		}
		err = Db.Exec(sql).Error
		return
	}
	return
}

func main() {
	GetThread()
}
